﻿namespace MicroCloud.Utils
{
    /// <summary>
    /// 异步读写锁
    /// </summary>
    public class AsyncReaderWriterLock
    {
        //字段
        private readonly Task<Releaser> _readerReleaser;
        private readonly Queue<TaskCompletionSource<Releaser>> _waitingWriters = new Queue<TaskCompletionSource<Releaser>>();
        private readonly Task<Releaser> _writerReleaser;
        private int _readersWaiting;
        private int _status;
        private TaskCompletionSource<Releaser> _waitingReader =
            new TaskCompletionSource<Releaser>();

        /// <summary>
        /// 初始化一个异步读写锁（<see cref="AsyncReaderWriterLock"/>）的新实例
        /// </summary>
        public AsyncReaderWriterLock()
        {
            _readerReleaser = Task.FromResult(new Releaser(this, false));
            _writerReleaser = Task.FromResult(new Releaser(this, true));
        }


        /// <summary>
        /// 异步阅读器锁定
        /// </summary>
        /// <returns></returns>
        public Task<Releaser> ReaderLockAsync()
        {
            lock (_waitingWriters)
            {
                if (_status >= 0 && _waitingWriters.Count == 0)
                {
                    ++_status;
                    return _readerReleaser;
                }

                ++_readersWaiting;
                return _waitingReader.Task.ContinueWith(t => t.Result);
            }
        }

        /// <summary>
        /// 异步写入器锁定
        /// </summary>
        /// <returns></returns>
        public Task<Releaser> WriterLockAsync()
        {
            lock (_waitingWriters)
            {
                if (_status == 0)
                {
                    _status = -1;
                    return _writerReleaser;
                }
                else
                {
                    var waiter = new TaskCompletionSource<Releaser>();
                    _waitingWriters.Enqueue(waiter);
                    return waiter.Task;
                }
            }
        }

        /// <summary>
        /// 阅读器释放
        /// </summary>
        private void ReaderRelease()
        {
            TaskCompletionSource<Releaser> toWake = null;

            lock (_waitingWriters)
            {
                --_status;
                if (_status == 0 && _waitingWriters.Count > 0)
                {
                    _status = -1;
                    toWake = _waitingWriters.Dequeue();
                }
            }

            if (toWake != null)
            {
                toWake.SetResult(new Releaser(this, true));
            }
        }

        /// <summary>
        /// 写入器释放
        /// </summary>
        private void WriterRelease()
        {
            TaskCompletionSource<Releaser> toWake = null;
            bool toWakeIsWriter = false;

            lock (_waitingWriters)
            {
                if (_waitingWriters.Count > 0)
                {
                    toWake = _waitingWriters.Dequeue();
                    toWakeIsWriter = true;
                }
                else if (_readersWaiting > 0)
                {
                    toWake = _waitingReader;
                    _status = _readersWaiting;
                    _readersWaiting = 0;
                    _waitingReader = new TaskCompletionSource<Releaser>();
                }
                else
                {
                    _status = 0;
                }
            }

            if (toWake != null)
            {
                toWake.SetResult(new Releaser(this, toWakeIsWriter));
            }
        }

        /// <summary>
        /// 释放资源的包装
        /// </summary>
        public struct Releaser : IDisposable
        {
            //字段
            private readonly AsyncReaderWriterLock _toRelease;
            private readonly bool _writer;

            /// <summary>
            /// 释放器
            /// </summary>
            /// <param name="toRelease"></param>
            /// <param name="writer"></param>
            internal Releaser(AsyncReaderWriterLock toRelease, bool writer)
            {
                _toRelease = toRelease;
                _writer = writer;
            }

            /// <summary>
            /// 执行与释放、释放或重置非托管资源相关联的应用程序定义的任务
            /// </summary>
            /// <filterpriority>2</filterpriority>
            public void Dispose()
            {
                if (_toRelease != null)
                {
                    if (_writer)
                    {
                        _toRelease.WriterRelease();
                    }
                    else
                    {
                        _toRelease.ReaderRelease();
                    }
                }
            }

        }

    }
}